package com.happy016.context;

/**
 * 2017-08-22 以下分析是第一次阅读Spring源码,自我感觉还是很浅,有点模棱两可,希望多看几遍能摸透Spring的奥秘
 *
 * @since 2017-09-04 看完bean加载过程,bean的注册,bean的依赖注入
 *      优雅的代码,完美的注释,算是摸到了Spring大门的门把手,需要继续一遍一遍的,逐个方法去解析.
 * @author Linyb
 */
public class ClassPathXmlApplicationContext_ {

    //ApplicationContext IOC容器
    //示例 ClassPathXmlApplicationContext

    //一.载入BeanDefinition
        //查看ClassPathXmlApplicationContext类,看到里面就是几个构造器,最后回到一个构造器
            /*
             *	public ClassPathXmlApplicationContext(String[] paths, Class<?> clazz,
             *	                   @Nullable ApplicationContext parent)throws BeansException {
		     *      super(parent);
		     *      Assert.notNull(paths, "Path array must not be null");
		     *      Assert.notNull(clazz, "Class argument must not be null");
		     *      this.configResources = new Resource[paths.length];
		     *      for (int i = 0; i < paths.length; i++) {
			 *           this.configResources[i] = new ClassPathResource(paths[i], clazz);
		     *      }
		     *      refresh();//加载IOC容器的入口
	         *  }
             */
       //再查看几个容器,如FileSystemXmlApplicationContext,看到跟ClassPathXmlApplicationContext差不多
             /*
              * 	public FileSystemXmlApplicationContext(String[] configLocations,
              *     	boolean refresh, @Nullable ApplicationContext parent)throws BeansException {
              *
		      *        super(parent);
		      *        setConfigLocations(configLocations);
		      *        if (refresh) {
			  *            refresh();//加载IOC容器的入口
		      *        }
	          *     }
              */
       //最基础实现的容易都是构造几个构造器,通过构造器来实现,具体的refresh()由父类来统一实现.

       //父类AbstractApplicationContext的refresh()方法
       /*
           public void refresh() throws BeansException, IllegalStateException {
		       synchronized (this.startupShutdownMonitor) {
			        // (源码注释:Prepare this context for refreshing.)
			        // 准备工作,比如getEnvironment,initPropertySources,顾名思义,
			        // 其中initPropertySources 初始化Sources 是走各自子类的属性配置,(源码注释:For subclasses: do nothing by default.)
			        // 比如ClassPathXml加载类工作路径,FileSystemXml加载系统文件xml
			        prepareRefresh();

			        // (源码注释:Tell the subclass to refresh the internal bean factory.)
			        // 告诉各自的子类 refresh BeanFactory
			        // obtainFreshBeanFactory()方法里面,首先执行了refreshBeanFactory()方法
			        // refreshBeanFactory()做的事,
			        //   如果有BeanFactory,destroy close 已有的,然后创建新的,加载新BeanFactory的BeanDefinitions
			        //   BeanDefinitions通过一个BeanDefinitionsReader加载了initPropertySources初始化的各自子类对应的资源配置
			        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			        // Prepare the bean factory for use in this context.
			        // 这个方法里面对BeanFactory的使用做准备,比如使用什么ClassLoader
			        prepareBeanFactory(beanFactory);

			        try {
				        // Allows post-processing of the bean factory in context subclasses.
				        postProcessBeanFactory(beanFactory);

				        // Invoke factory processors registered as beans in the context.
				        invokeBeanFactoryPostProcessors(beanFactory);

				        // Register bean processors that intercept bean creation.
				        registerBeanPostProcessors(beanFactory);

				        // Initialize message source for this context.
				        // 初始化 message source,包括从双亲获取
				        initMessageSource();

				        // Initialize event multicaster for this context.
				        initApplicationEventMulticaster();

				        // Initialize other special beans in specific context subclasses.
				        onRefresh();

				        // Check for listener beans and register them.
				        registerListeners();

				        // Instantiate all remaining (non-lazy-init) singletons.
				        finishBeanFactoryInitialization(beanFactory);

				        // Last step: publish corresponding event.
				        finishRefresh();

			        }catch (BeansException ex) {
				        if (logger.isWarnEnabled()) {
					        logger.warn("Exception encountered during context initialization - " +
							    "cancelling refresh attempt: " + ex);
				        }

				        // Destroy already created singletons to avoid dangling resources.
				        destroyBeans();

	        			// Reset 'active' flag.
			        	cancelRefresh(ex);

				        // Propagate exception to caller.
				        throw ex;
			        }

	        		finally {
			        	// Reset common introspection caches in Spring's core, since we
				        // might not ever need metadata for singleton beans anymore...
				        resetCommonCaches();
			        }
		        }
	        }
        */



       //obtainFreshBeanFactory()里面做了很多加载的前提
       //其中核心的loadBeanDefinitions(beanFactory);里面包含了加载资源配置,如xml配置等,并且按照一定规则注册到IOC容器中
       //loadBeanDefinitions里面做了 doLoadBeanDefinitions操作 ,做doLoad操作的时候,调用了registerBeanDefinitions
       //registerBeanDefinitions这个操作是由 BeanDefinitionDocumentReader 来完成.
       //其中 BeanDefinitionDocumentReader 利用了BeanDefinitionParserDelegate进行
       //三个方法完成:preProcessXml parseBeanDefinitions postProcessXml
       /* 总结:
        * loadBeanDefinitions <---beanFactory
        *     |
        *     |<-- doLoadBeanDefinitions
        *                |
        *                | <---registerBeanDefinitions
        *                           |
        *                           |<---BeanDefinitionsDocumentReader
        *                                     |
        *                                     |<--BeanDefinitionParserDelegate
        *                                               |
        *                                               |<-- preProcessXml(Element root)
        *                                               |<-- parseBeanDefinitions(Element root,BeanDefinitionParserDelegate delegate)
        *                                               |<-- postProcessXml(Element root)
        *
        * BeanDefinitionParserDelegate 类里面定义很多 xml标签,比如最常见的<bean />
        *   解析入口:
        *       parseBeanDefinitionElement(Element ele,String beanName,BeanDefinition containingBean)这个方法,里面做一系列的bean构造.
        *           |
        *           |<--parseBeanDefinitionAttributes(ele,beanName,containBean,bd);
        *           |                           |<---解析bean属性值attributes,比如单例,scope,lazyInit等等
        *           |<--parseMetaElements(ele,bd);
        *           |                           |<---对应meta标签的配置设置
        *           |<--parseLookupOverrideSubElements(ele,bd.getMethodOverrides());
        *           |                           |<---解析lookup-method方法<bean><look-up></look-up></bean>
        *           |<--parseReplacedMethodSubElements(ele,bd.getMethodOverrides());
        *           |                           |<---解析replaced-method方法
        *           |<--parseConstructorArgElements(ele,bd);
        *           |                           |<---解析bean构造器  constructor-arg
        *           |<--parsePropertyElements(ele,bd);
        *           |                           |<---解析bean的property设置 <bean><property></property></bean>
        *           |<--parseQualifierElements(ele,bd);
        *           |                           |<---解析qualifier <bean><qualifier value=""/></bean>
        *
        *      上面的bd是AbstractBeanDefinition,实现了接口BeanDefinition,对BeanDefinition进行了一些扩展,实现了一些自动装载等.
        *
        *   总结:
        *       个人理解上面那些步骤的解析,是按顺序来的
        *       举<bean class="" id="" init-method=""><property name="" value=""/></bean>
        *           Element ele 代表一个xml节点,beanName是要解析的bean标识
        *           先解析了bean的属性,比如id,class,init-method属性.(parseBeanDefinitionAttributes)
        *           然后解析里面具体的property.(parsePropertyElements),可见是一个顺序的过程.
        *     从上可以看出IOC容器只是对bean对象数据的准备工作,还未对依赖注入进行装载.
        *     上面那些操作的BeanDefinition,Spring把他都存放在一个线程安全的Map里面:beanDefinitionMap
        *     private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
        */

       /*
        * 有了Bean上面的工作,就可以实现Bean依赖注入.
        * Bean的依赖注入:
        *  BeanFactory及其子类里面有一系列getBean(arg)方法;
        *  第一次向IOC容器获取Bean时触发Bean的依赖注入.
        *
        *  AbstractBeanFactory.getBean()
        *       |
        *       |<--createBean()
        *               |
        *               |<--doCreateBean()
        *                       |
        *                       |<--populateBean()//完成依赖注入的过程
        *                               |
        *                               |<--applyPropertyValues()
        *                                       |
        *                                       |<--convertIfNecessary();
        *                                               |<--这里面做了很多的操作;
        *                                               |<--Bean里面引用其它Bean,注入其它Bean,
        *                                                       如@Autowire private IUserService userService;
        *                                               |<--Bean里面应用Map,注入Map,类推..
        *
        */



       /*
        *
        * 总结:IOC容器的基本工作原理
        *
        *       1.Resource统一定义Bean的信息,由ResourceLoader来完成Resource的加载.
        *       2.BeanDefinition根据Resource来定位,生成一个BeanDefinition.
        *           如果使用ApplicationContext上下文,本身提供了ResourceLoader的功能,
        *           ApplicationContext本身就是DefaultResourceLoader的子类.
        *       3.IOC容器的初始化
        *           refresh()入口.完成了对BeanDefinition的载入和注册,由BeanDefinitionLoader来完成这个功能.
        *           所有的BeanDefinition被维护在一个Map中.这个时候Bean只是完成了Bean的初始化,还未完成Bean之间的依赖.
        *       4.Bean之间的依赖注入
        *           第一次使用Bean的时候通过getBean()来获取,获取的同时完成依赖.
        *
        *
        */
}
